package main

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/knieriem/markdown"
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [FILE]\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(1)
}

func findSrcValue(props string) (start, end int) {
	stat := 0
	pos := 0
	pl := len(props)
	for pos < pl {
		c := props[pos]
		switch c {
		case ' ', '\t', '\r', '\n':
			if stat != 3 {
				stat = 0
			}
		case 's':
			if stat == 0 {
				stat = 1
			} else {
				stat = -1
			}
		case 'r':
			if stat == 1 {
				stat = 2
			} else {
				stat = -1
			}
		case 'c':
			if stat == 2 {
				stat = 3
			} else {
				stat = -1
			}
		case '"', '\'':
			if stat == 3 {
				start = pos + 1
				end = start + strings.IndexByte(props[start:], c)
				return
			}
		}
		pos++
	}
	return
}

func writeContent(fout *os.File, buf *bytes.Buffer, path string) {
	html := buf.String()
	for {
		// 查找 img 元素
		pos := strings.Index(html, "<img ")
		if -1 == pos {
			fout.WriteString(html)
			break
		}
		fout.WriteString(html[:pos+5])
		html = html[pos+5:]

		// 查询 img 元素的结束标签
		closePos := strings.Index(html, ">")
		if -1 == closePos {
			fout.WriteString(html)
			break
		}
		inImg := html[:closePos]
		html = html[closePos:]

		if len(inImg) == 0 {
			continue
		}

		srcStart, srcEnd := findSrcValue(inImg)
		if srcEnd <= srcStart {
			fout.WriteString(inImg)
			continue
		}

		if srcStart > 0 {
			fout.WriteString(inImg[:srcStart])
		}
		if strings.HasPrefix(inImg[srcStart:srcEnd], "data:image") {
			fout.WriteString(inImg[srcStart:srcEnd])
		} else {
			writeImage(fout, inImg[srcStart:srcEnd], path)
		}
		if srcEnd < len(inImg) {
			fout.WriteString(inImg[srcEnd:])
		}
	}
}

func writeImage(fout *os.File, src, path string) {
	data, err := ioutil.ReadFile(filepath.Join(path, src))
	if err != nil {
		fmt.Println(err)
		fout.WriteString(src)
		return
	}
	fout.WriteString("data:image/")
	switch strings.ToLower(filepath.Ext(src)) {
	case ".png":
		fout.WriteString("png")
	case ".gif":
		fout.WriteString("gif")
	case ".bmp":
		fout.WriteString("bmp")
	default:
		fout.WriteString("jpeg")
	}
	fout.WriteString(";base64,")
	fout.WriteString(base64.StdEncoding.EncodeToString(data))
}

func out(fout *os.File, tmpl, title, css string, buf *bytes.Buffer, path string) {
	for {
		posLeft := strings.Index(tmpl, "{$")
		if -1 == posLeft {
			fout.WriteString(tmpl)
			break
		}
		posRight := strings.Index(tmpl, "}")
		if posRight < posLeft {
			fout.WriteString(tmpl)
			break
		}
		if posLeft > 0 {
			fout.WriteString(tmpl[:posLeft])
		}
		switch tmpl[posLeft : posRight+1] {
		case "{$TITLE}":
			fout.WriteString(title)
		case "{$CSS}":
			fout.WriteString(css)
		case "{$CONTENT}":
			writeContent(fout, buf, path)
		default:
			fout.WriteString(tmpl[posLeft : posRight+1])
		}
		if posRight+1 >= len(tmpl) {
			break
		}
		tmpl = tmpl[posRight+1:]
	}
}

func readFileToString(file string) string {
	buf, err := ioutil.ReadFile(file)
	if nil != err {
		log.Println(err)
		return ""
	}
	return string(buf)
}

func main() {

	// 参数
	var (
		title  = flag.String("title", "", "html title, default is file basename")
		css    = flag.String("css", "", "css file")
		tmpl   = flag.String("tmpl", "", "html template file, vars: {$TITLE}, {$CSS}, {$CONTENT}")
		output = flag.String("out", "", "output file")
	)
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() <= 0 {
		usage()
	}

	// 输入
	mdPath := flag.Arg(0)
	fin, err := os.Open(mdPath)
	if err != nil {
		log.Fatal(err)
	}
	defer fin.Close()

	// 输出
	if "" == *output {
		*output = mdPath + ".html"
	}
	fout, err := os.OpenFile(*output, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		log.Fatal(err)
	}
	defer fout.Close()

	// 解释
	var opt markdown.Extensions
	buf := bytes.NewBuffer(make([]byte, 0))
	p := markdown.NewParser(&opt)
	w := bufio.NewWriter(buf)
	p.Markdown(fin, markdown.ToHTML(w))
	w.Flush()

	// 标题
	if "" == *title {
		*title = filepath.Base(mdPath)
	}

	// tmpl and css
	var tmplContent, cssContent string
	if "" != *tmpl {
		tmplContent = readFileToString(*tmpl)
	}
	if "" == tmplContent {
		tmplContent = defaultTemplate
	}
	if "" != *css {
		cssContent = readFileToString(*css)
	}
	if "" == cssContent {
		cssContent = defaultCss
	}

	out(fout, tmplContent, *title, cssContent, buf, filepath.Dir(mdPath))
}
